Gonzalo Alvis¶

Usar urbanpy para descargar datos de una ciudad

Los markdowns los hice para enterarme de que estaba haciendo.

Librerias¶

In [1]:
import warnings
import sys
import urbanpy as up
import pandas as pd
import plotly
import plotly.express as px
import plotly.graph_objects as go
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
from tqdm.notebook import tqdm
import geopandas as gpd

sys.path.append("..")
warnings.filterwarnings("ignore") 
tqdm.pandas()

Mapa¶

Descarga de OSM (open street maps) el mapa de la ciudad que especificas.

In [2]:
ciudad = up.download.nominatim_osm(
    "Barcelona, España"
)  # expected_position is 0 by default
In [3]:
ciudad.plot()

plt.show()
No description has been provided for this image

population data¶

Esta funcion consulta las bases de datos que hay de este pais (luego se filtrará).

Todo lo que tiene hdx se refiere al humanitarian data exchange (base de datos)

In [4]:
city_resources=up.download.search_hdx_dataset("Spain")
city_resources
Out[4]:
created name population size_mb url
id
0 2020-03-04 esp_general_2020_csv.zip Overall population density 222.35 https://data.humdata.org/dataset/80d0519e-0eaf...
8 2019-09-23 esp_children_under_five_2020_csv.zip Children (ages 0-5) 221.25 https://data.humdata.org/dataset/80d0519e-0eaf...
9 2019-09-23 esp_elderly_60_plus_2020_csv.zip Elderly (ages 60+) 222.65 https://data.humdata.org/dataset/80d0519e-0eaf...
10 2019-09-23 esp_men_2020_csv.zip Men 222.07 https://data.humdata.org/dataset/80d0519e-0eaf...
11 2019-09-23 esp_women_2020_csv.zip Women 222.16 https://data.humdata.org/dataset/80d0519e-0eaf...
12 2019-09-23 esp_women_of_reproductive_age_15_49_2020_csv.zip Women of reproductive age (ages 15-49) 222.44 https://data.humdata.org/dataset/80d0519e-0eaf...
In [5]:
pop_spn=up.download.get_hdx_dataset(city_resources,0)
pop_spn.head()
Out[5]:
longitude latitude esp_general_2020
0 -1.269444 44.0 0.673977
1 -1.268611 44.0 0.673977
2 -1.160833 44.0 0.536216
3 -1.158333 44.0 0.536216
4 -1.006944 44.0 0.539089

Conversion a puntos y hexagonos¶

Ok, esta parte hace el filtro de la informacion para la ciudad. O sea, la informacion es de españa, pero esta parte lo que hace es filtrar de acuerdo a las dimensiones de la ciudad (Barcelona). Ademas, crea los hexagonos (esta figura es perfecta para la ubicacion espacial porque esta igual de cerca que todos sus vecinos.)

La resolucion influencia en el numero de hexagonos. La diferencia entre 8 y 9 en el numero de hexagonos me parece destacable, para barcelona por lo menos. En el demo se ve que usa el 7 para quito.

In [6]:
pop_ba=up.geom.filter_population(pop_spn,ciudad)
In [7]:
pop_ba.plot()
Out[7]:
<Axes: >
No description has been provided for this image
In [8]:
#11 es demasiado detalle, ademas, con mas resolucion tarda mas de 2 segundos. El ideal es un punto entre 9 y 8. Usare el 8
hex_ba=up.geom.gen_hexagons(resolution=8,city=ciudad)
hex_ba.shape
Out[8]:
(144, 2)
In [9]:
hex_ba.plot()
Out[9]:
<Axes: >
No description has been provided for this image

Mergeando layers¶

El mergeo se encarga de cruzar las bases, o sea, colorear los hexagonos con la informacion de la poblacion de barcelona españa por ejemplo. En este mapa en particular, se ve que las zonas en gris, o con baja densidad poblacional son las areas verdes como observatorios, montañas o atracciones de ese estilo.

In [10]:
pop_ba.head()
Out[10]:
longitude latitude esp_general_2020 geometry
9828392 2.052500 41.467778 11.406598 POINT (2.05250 41.46778)
9828393 2.053333 41.467778 5.703299 POINT (2.05333 41.46778)
9828394 2.053611 41.467778 5.703299 POINT (2.05361 41.46778)
9828395 2.053889 41.467778 5.703299 POINT (2.05389 41.46778)
9828396 2.054167 41.467778 5.703299 POINT (2.05417 41.46778)
In [11]:
hex_ba=up.geom.merge_shape_hex(hex_ba,pop_ba,agg={"esp_general_2020":"sum"})
In [12]:
hex_ba.plot(
    "esp_general_2020", legend=True, missing_kwds={"color": "grey"}
)
plt.title("Barcelona - H3 res: 8")
plt.axis(False)
plt.tight_layout()
No description has been provided for this image
In [13]:
fs, metadata = up.download.overpass(
    type_of_data="node",
    mask=ciudad,
    query={
        "amenity": ["marketplace"],
        "shop": [
            "supermarket",
            "kiosk",
            "mall",
            "convenience",
            "butcher",
            "greengrocer",
        ],
    },
)

El demo en este momento consulta el API de overpass para obtener POI (puntos de interes). Dejó un link a la wiki de OSM. He probado una query para universidades en Barcelona

In [14]:
fs, metadata = up.download.overpass(
    type_of_data="node",
    mask=ciudad,
    query={
        "amenity": ["university"],
        
    },
)

Aca busco si estan los nombres de las universidades para el grafico de mas tarde

In [15]:
for i in fs['tags']:
    try:
        print(i['name'])
    except:
        print(i)
Escola de Noves Tecnologies Interactives
Facultat de Física
Facultat de Química
Facultat de Farmàcia
Facultat d'Economia i Empresa
Facultat de Biologia
Col·legi Major Penyafort
IGEMA
Universitat de Barcelona School of Economics
Acadèmia SOL
Alberg Sea Point Hostel
UPC Facultat de Nàutica de Barcelona
Escola Superior de Relacions Públiques
EAE Bussines School
Acadèmia ASES
Escola Universitària Salesiana de Sarrià EUSS
Universitat Pompeu Fabra–Campus del Poblenou
INSA. Business, Marketing & Communication School
Arquitectura la Salle
Enginyeria i Arquitectura la Salle
Academia CEUS
IEEE BCN Student branch
Residencia Universitaria Torre Girona
IESE Business School
Institut Químic de Sarrià
Enginyeria i Arquitectura La Salle
ISDE Law School
Facultat d'Educació
Facultat de Psicologia
In [16]:
#numero de resultados de la consulta, en este caso numero de universidades que overpass encuentra en Barcelona
fs.shape
Out[16]:
(29, 7)

Entiendo que esta parte se encarga de abrir un servidor de osrm (open source routing machine) en Docker (no lo consiguio, tuve que hacerlo manualmente).

Habiendo entrado en el directorio del windows_download

.\windows_download.ps1 osrm_routing_server spain europe foot

El servidor encontrará la distancia entre cada hexagono y el POI mas cercano. Por eso tiene 144 consultas en la casilla de travel_times, porque son 144 hexagonos

In [17]:
up.routing.start_osrm_server("spain", "europe", "foot")
Starting server ...
Server was started succesfully
In [19]:
hex_barcelona_access = up.accessibility.travel_times(hex_ba, fs, "university")
hex_barcelona_access.head()
  0%|          | 0/144 [00:00<?, ?it/s]
Out[19]:
hex geometry esp_general_2020 lon lat nearest_university_ix distance_to_nearest_university duration_to_nearest_university duration_to_nearest_university_label
0 883944635dfffff POLYGON ((2.05686 41.43592, 2.05569 41.43116, ... 172.877483 2.061539 41.433011 23 9.0403 108.820000 90-120
1 8839446309fffff POLYGON ((2.05333 41.42163, 2.05215 41.41687, ... 350.330407 2.058001 41.418721 23 7.3502 88.538333 60-90
2 8839446355fffff POLYGON ((2.06036 41.42825, 2.05918 41.42348, ... 34.575497 2.065034 41.425339 23 7.3271 88.260000 60-90
3 8839446289fffff POLYGON ((2.17635 41.46054, 2.17516 41.45577, ... 7157.127811 2.181025 41.457623 27 5.1782 62.220000 60-90
4 8839446ad3fffff POLYGON ((2.12678 41.32632, 2.12560 41.32155, ... 8435.343412 2.131452 41.323400 12 7.2841 88.486667 60-90
In [20]:
up.routing.stop_osrm_server("spain", "europe", "foot")
Server was stoped succesfully

Interactive map¶

In [21]:
plotly.offline.init_notebook_mode()

El mapa del demo para ver la demografia (con plotly). Le agregué el zoom porque era incomodo de ver

In [27]:
fig = up.plotting.choropleth_map(
    hex_ba,
    "esp_general_2020",
    title="Estimated Spain Population - 2020",
    width=800,
    height=800,
)

# Make space for the title
fig.update_layout(margin=dict(l=0, r=0, b=0),mapbox=dict(zoom=11))

fig.show()

aca ordena los tiempos de viaje en categorias, para hacer el mapa para visualizar barcelona y los tiempos promedio que tardan para llegar a la universidad mas cercana a pie

In [23]:
# Get ordered category labels
category_orders = (
    hex_barcelona_access["duration_to_nearest_university_label"].unique().sort_values()
)
category_orders.categories
Out[23]:
Index(['0-15', '15-30', '30-45', '45-60', '60-90', '90-120'], dtype='object')
In [24]:
hex_barcelona_access_filtered_pop = hex_barcelona_access.query(
    "esp_general_2020 > 0"
).reset_index(drop=True)
In [26]:
fig = up.plotting.choropleth_map(
    hex_barcelona_access_filtered_pop,
    color_column="duration_to_nearest_university_label",
    color_discrete_sequence=px.colors.sequential.Plasma_r,
    category_orders={"duration_to_nearest_university_label": category_orders},
    labels={"duration_to_nearest_university_label": "Minutes"},
    title="Time to Nearest University (Walking Distance)",
    width=800,
    height=800,
)

# Make space for the title
fig.update_layout(margin=dict(l=0, r=0, b=0))

# Remove the hexagon outlines to make the map clearer
fig.update_traces(marker_line_width=0)

fs['name'] = fs['tags'].apply(lambda x: x.get('name', 'Unknown'))
fs['geometry'] = gpd.points_from_xy(fs['lon'], fs['lat'])

scatter_trace = go.Scattermapbox(
    lat=fs['lat'],
    lon=fs['lon'],
    mode='markers',
    marker=go.scattermapbox.Marker(
        size=10,
        color='blue',
        opacity=0.7
    ),
    text=fs['name'],
    hoverinfo='text',
    showlegend=False
)

fig.add_trace(scatter_trace)

fig.update_layout(
    mapbox=dict(zoom=11)
)

fig.show()

Finalmente, mi mapa es de barcelona con los tiempos (en minutos) que toma llegar entre cada hexagono a la universidad mas cercana (a pie). Cada punto azul es una universidad, con su nombre.